#include "SAXHelper.h"
#include <expat.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <stdio.h>
#include <string.h>

namespace phnq
{
	namespace kit
	{
		namespace xml
		{
			struct elmnt_info
			{
				string name;
				map<string,string> attrs;
			};
			
			struct parse_info
			{
				SAXHelperDelegate *delegate;
				vector<elmnt_info> elmntStack;
			};
			
			static string getPath(vector<elmnt_info> elmntStack)
			{
				stringbuf buf;
				for(vector<elmnt_info>::iterator it=elmntStack.begin(); it!=elmntStack.end(); it++)
				{
					buf.sputc('/');
					elmnt_info ei = *it;
					buf.sputn(ei.name.c_str(), ei.name.length());
				}
				return buf.str();
			}
			
			static void XMLCALL startTag(void *userData, const char *name, const char **attrs)
			{
				parse_info *info = (parse_info*)userData;
				string tagName(name);

				map<string,string> attrsMap;
				while(*attrs)
				{
					string n(*(attrs++));
					string v(*(attrs++));
					attrsMap[n] = v;
				}
				
				elmnt_info ei;
				ei.name = tagName;
				ei.attrs = attrsMap;
				info->elmntStack.push_back(ei);
				
				info->delegate->startTag(tagName, getPath(info->elmntStack), ei.attrs);
			}
			
			static void XMLCALL endTag(void *userData, const char *name)
			{
				parse_info *info = (parse_info*)userData;
				
				elmnt_info ei = info->elmntStack.back();

				string tagName(name);
				
				info->delegate->endTag(tagName, getPath(info->elmntStack), ei.attrs);

				info->elmntStack.pop_back();
			}
			
			static void XMLCALL charData(void *userData, const XML_Char *s, int len)
			{
				parse_info *info = (parse_info*)userData;
				elmnt_info ei = info->elmntStack.back();
				
				char c[len+1];
				strncpy(c, s, len);
				c[len] = '\0';
				
				string data(c);

				size_t a = data.find_first_not_of(" \t\n\r");
				if(a != string::npos)
				{
					size_t b = data.find_last_not_of(" \t\n\r");
					info->delegate->charData(data.substr(a, b-a+1), getPath(info->elmntStack), ei.attrs);
				}
			}
			
			SAXHelper::SAXHelper(SAXHelperDelegate *delegate)
			{
				this->delegate = delegate;
			}

			SAXHelper::~SAXHelper()
			{
				// a little destructor action
			}

			void SAXHelper::parse(string filename)
			{
				int length;
				int done = 0;
				ifstream is;
				char buff[128];
				XML_Parser parser;

				parser = XML_ParserCreateNS(NULL, '.');
				
				parse_info info;
				info.delegate = this->delegate;

				XML_SetUserData(parser, &info);
				XML_SetElementHandler(parser, startTag, endTag);

				// if(charData)
					XML_SetCharacterDataHandler(parser, charData);

				// if(procInst)
				// 	XML_SetProcessingInstructionHandler(parser, procInst);

				is.open (filename.c_str(), ios::in);

				do
				{
					length = is.readsome(buff, 128);
					XML_Parse(parser, buff, length, done);
				}
				while(length && !done);

				is.close();
			}
		}
	}
}
